package com.guoguo.utils;

import com.aliyun.oss.ClientException;
import com.aliyun.oss.HttpMethod;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.*;
import com.guoguo.base.StaticResource;
import com.guoguo.utils.aliOss.AliOssImage;
import com.guoguo.utils.aliOss.AliOssImageConfig;
import com.guoguo.utils.aliOss.AliOssImageFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URL;
import java.util.*;
import java.util.concurrent.*;

/**
 * Created by Administrator on 2017/8/23 0023.
 */
public class AliOssUtils extends Thread {
    private static final Logger logger = LoggerFactory.getLogger(AliOssUtils.class);

    //aliyun配置文件
    private static String configFileName = "aliyun.properties";
    //外网访问地址
    private static String endpoint = "";
    //
    private static String accessKeyId = "";
    //
    private static String accessKeySecret = "";
    //存储空间名
    private static String bucketName = "";
    //并发数
    private static int taskNum = 5;
    //分片大小
    private static long partSize = 1024 * 1024 * 1;
    //是否开启断点续传
    private static boolean isCheckPoint = true;
    private static int max = 0;

    private static int imgWidth = 600;
    private static int imgHeight = 400;
    //过期时间（按秒计算）
    private static int expirTime = 60 * 60 * 6;

    private static OSSClient ossClient;


    /**
     * 初始化参数
     */
    private static void initConfig() {
        if (bucketName == null || "".equals(bucketName)) {
            bucketName = PropertyUtils.getProperty(configFileName, "ali.bucketName");
        }
        if (endpoint == null || "".equals(endpoint)) {
            endpoint = PropertyUtils.getProperty(configFileName, "ali.endpoint");
        }
        if (accessKeyId == null || "".equals(accessKeyId)) {
            accessKeyId = PropertyUtils.getProperty(configFileName, "ali.accessKeyId");
        }
        if (accessKeySecret == null || "".equals(accessKeySecret)) {
            accessKeySecret = PropertyUtils.getProperty(configFileName, "ali.accessKeySecret");
        }
        if (max == 0) {
            max = Integer.valueOf(PropertyUtils.getProperty(configFileName, "ali.maxKeys"));
        }
    }

    private static OSSClient getOssClient() {
        initConfig();
        if (ossClient == null) {
            return new OSSClient(endpoint, accessKeyId, accessKeySecret);
        }
        return ossClient;
    }

    /**
     * 初始化上传配置
     *
     * @param uploadFile
     * @return
     */
    private static UploadFileRequest uploadInitconfig(String uploadFile) {
        UploadFileRequest uploadFileRequest = new UploadFileRequest(bucketName, StringUtils.generateOssKey(uploadFile));
        // 待上传的本地文件
        uploadFileRequest.setUploadFile(uploadFile);
        // 设置并发下载数，默认1
        uploadFileRequest.setTaskNum(taskNum);
        // 设置分片大小，默认100KB
        uploadFileRequest.setPartSize(partSize);
        // 开启断点续传，默认关闭
        uploadFileRequest.setEnableCheckpoint(isCheckPoint);
        return uploadFileRequest;
    }

    /**
     * 实现多线程，并掉用上传方法
     */
    static class UploadCallable implements Callable<String> {
        private String fileName;

        UploadCallable(String fileName) {
            this.fileName = fileName;
        }

        @Override
        public String call() throws Exception {
            logger.debug("==================>    文件：" + this.fileName + "上传启动....");
            String url = AliOssUtils.upLoadFile(this.fileName);
            logger.debug("==================>    文件：" + this.fileName + "上传结束....");
            return url;
        }
    }


    /**
     * 批量上传文件
     * 一次性最多上传10个文件
     *
     * @param list
     * @return
     */
    public static List<String> upLoadFileList(List<String> list) {
        List<String> keyList = new ArrayList<String>();
        logger.debug("==================>    批量上传启动....");
        //线程池大小
        int taskSize = list.size() > 10 ? 10 : list.size();
        ExecutorService pool = Executors.newFixedThreadPool(taskSize);
        // 创建多个有返回值的任务
        List<Future> futureList = new ArrayList<Future>();
        for (String s : list) {
            Callable c = new UploadCallable(s);
            // 执行任务并获取Future对象
            Future f = pool.submit(c);
            futureList.add(f);
        }
        // 关闭线程池  
        pool.shutdown();


        for (Future future : futureList) {
            try {
                keyList.add(future.get().toString());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
        logger.debug("==================>    批量上传结束....");
        return keyList;
    }

    /**
     * 单个文件上传
     * 断点续传
     *
     * @param updateFile
     */
    public static String upLoadFile(String updateFile) {

        String file = new String();
        OSSClient ossClient = getOssClient();
        try {
            UploadFileResult uploadResult = ossClient.uploadFile(uploadInitconfig(updateFile));

            CompleteMultipartUploadResult multipartUploadResult = uploadResult.getMultipartUploadResult();

            file = multipartUploadResult.getKey();

        } catch (OSSException oe) {
            logger.info("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            logger.info("Error Message: " + oe.getErrorCode());
            logger.info("Error Code:       " + oe.getErrorCode());
            logger.info("Request ID:      " + oe.getRequestId());
            logger.info("Host ID:           " + oe.getHostId());
        } catch (ClientException ce) {
            logger.info("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            logger.info("Error Message: " + ce.getMessage());
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        } finally {
            ossClient.shutdown();
        }
        return file;
    }

    /**
     * 判断Bucket是否存在
     *
     * @param bucket
     * @return
     */
    public static boolean isValiBucket(String bucket) {
        OSSClient ossClient = getOssClient();
        boolean flag = ossClient.doesBucketExist(bucket);
        ossClient.shutdown();
        return flag;
    }

    /**
     * 判断文件是否存在
     *
     * @param fileName
     * @return
     */
    public static boolean isValiFile(String bucket, String fileName) {
        boolean flag = false;
        OSSClient ossClient = getOssClient();
        if (bucket == null || "".equals(bucket)) {
            bucket = bucketName;
        }
        flag = ossClient.doesObjectExist(bucket, fileName);
        ossClient.shutdown();
        return flag;
    }

    /**
     * 判断文件是否存在
     *
     * @param fileName
     * @return
     */
    public static boolean isValiFile(String fileName) {
        return isValiFile(null, fileName);
    }

    /**
     * 获得oss上指定path下的文件
     * 默认一百条数据
     *
     * @param bucket
     * @param path
     * @param maxKeys
     */
    public static List<String> getOssFileList(String bucket, String path, int maxKeys) {
        OSSClient ossClient = getOssClient();
        if (bucket == null || "".equals(bucket)) {
            bucket = bucketName;
        }
        ObjectListing objectListing = ossClient.listObjects(bucket, path);
        if (maxKeys == 0) {
            maxKeys = max;
        }
        objectListing.setMaxKeys(maxKeys);
        List<OSSObjectSummary> sums = objectListing.getObjectSummaries();
        List<String> list = new ArrayList<String>();
        for (OSSObjectSummary sum : sums) {
            list.add(sum.getKey());
        }
        return list;
    }

    /**
     * 获得oss上指定path下的文件
     * 默认一百条数据
     *
     * @param path
     * @param maxKeys
     */
    public static List<String> getOssFileList(String path, int maxKeys) {
        return getOssFileList(null, path, max);
    }

    /**
     * 获得oss上指定path下的文件
     * 默认一百条数据
     *
     * @param path
     */
    public static List<String> getOssFileList(String path) {
        return getOssFileList(null, path, max);
    }

    /**
     * 批量删除oss文件
     *
     * @param bucket
     * @param fileList
     */
    public static void delOssFiles(String bucket, List<String> fileList) {
        OSSClient ossClient = getOssClient();
        if (bucket == null || "".equals(bucket)) {
            bucket = bucketName;
        }
        DeleteObjectsResult deleteObjectsResult = ossClient.deleteObjects(new DeleteObjectsRequest(bucket).withKeys(fileList));
        List<String> deletedObjects = deleteObjectsResult.getDeletedObjects();
        // 关闭client
        ossClient.shutdown();
    }

    /**
     * 批量删除oss文件
     *
     * @param fileList
     */
    public static void delOssFile(List<String> fileList) {
        delOssFiles(null, fileList);
    }

    /**
     * 删除oss单个文件
     *
     * @param bucket
     * @param fileName
     */
    public static void delOssFile(String bucket, String fileName) {
        List<String> list = new ArrayList<String>();
        list.add(fileName);
        delOssFiles(bucket, list);
    }

    /**
     * 删除oss单个文件
     *
     * @param fileName
     */
    public static void delOssFile(String fileName) {
        delOssFile(null, fileName);
    }

    /**
     * 拷贝oss 文件
     *
     * @param bucket
     * @param fileName
     * @param destBucket
     * @param destFileName
     */
    public static void copyOssFile(String bucket, String fileName, String destBucket, String destFileName) {
        OSSClient ossClient = getOssClient();
        CopyObjectResult result = ossClient.copyObject(bucket, fileName, destBucket, destFileName);
        logger.info("================>>>>>>>      ETag: " + result.getETag() + " LastModified: " + result.getLastModified());
        // 关闭client
        ossClient.shutdown();
    }

    /**
     * 从oss上获取文件的访问路径。
     * 每个文件都需要获得权限才能访问。
     * 默认的过期时间是一个小时
     *
     * @param bucket
     * @param fileName
     * @param time
     * @return
     */
    private static Map<String, String> getImageUrl(String bucket, List<String> fileName, int time) {
        OSSClient ossClient = getOssClient();
        Map<String, String> urlMap = new HashMap<String, String>();
        if (time <= 0) {
            time = 60 * 60;
        }
        Date expiration = new Date(new Date().getTime() + time * 1000);
        if (bucket == null || "".equals(bucket)) {
            bucket = bucketName;
        }
        for (String s : fileName) {
            URL url = ossClient.generatePresignedUrl(bucket, s, expiration);
            urlMap.put(s, url.toString());
        }
        return urlMap;
    }

    /**
     * 获得图片文件的访问路径
     * 默认是一小时
     *
     * @param bucket
     * @param fileName
     * @param time
     * @return
     */
    public static String getImageUrl(String bucket, String fileName, int time) {
        List<String> list = new ArrayList<String>();
        list.add(fileName);
        Map<String, String> map = getImageUrl(bucket, list, time);
        return map.get(fileName);
    }

    /**
     * 获得图片文件的访问路径
     * 默认是一小时
     *
     * @param fileName
     * @param time
     * @return
     */
    public static String getImageUrl(String fileName, int time) {
        return getImageUrl(null, fileName, time);
    }

    /**
     * 获得图片文件的访问路径
     * 默认是一小时
     *
     * @param fileName
     * @return
     */
    public static String getImageUrl(String fileName) {
        return getImageUrl(null, fileName, 0);
    }

    /**
     * 批量获取文件的访问路径
     *
     * @param bucket
     * @param fileNames
     * @param time
     * @return
     */
    public static Map<String, String> getImageUrlList(String bucket, List<String> fileNames, int time) {
        Map<String, String> urlMap = getImageUrl(bucket, fileNames, time);
        return urlMap;
    }

    /**
     * 批量获取文件的访问路径
     *
     * @param fileNames
     * @param time
     * @return
     */
    public static Map<String, String> getImageUrlList(List<String> fileNames, int time) {
        Map<String, String> urlMap = getImageUrl(null, fileNames, time);
        return urlMap;
    }

    /**
     * 批量获取文件的访问路径
     *
     * @param fileNames
     * @return
     */
    public static Map<String, String> getImageUrlList(List<String> fileNames) {
        Map<String, String> urlMap = getImageUrl(null, fileNames, 0);
        return urlMap;
    }

    /**
     * 返回压缩图片访问url
     *
     * @param bucket
     * @param fileNames
     * @param processConfig
     * @param time
     */
    public static Map<String, String> imageProcess(String bucket, List<String> fileNames, List<AliOssImageConfig> processConfig, int time) {
        OSSClient ossClient = getOssClient();
        StringBuffer style = new StringBuffer();
        if (bucket == null || "".equals(bucket)) {
            bucket = bucketName;
        }
        //图片处理样式
        if (processConfig != null && processConfig.size() > 0) {
            style.append("image");
            for (AliOssImageConfig aliOssImageConfig : processConfig) {
                AliOssImage aliOssImage = AliOssImageFactory.createAliOssImage(aliOssImageConfig);
                if (aliOssImage.operatingString(aliOssImageConfig).indexOf("style") == -1) {
                    style.append(aliOssImage.operatingString(aliOssImageConfig));
                } else {
                    style.delete(0, style.length());
                    style.append(aliOssImage.operatingString(aliOssImageConfig));
                }
            }
        }
        //过期时间
        if (time == 0) {
            time = expirTime;
        }
        Date expiration = new Date(new Date().getTime() + 1000 * time);
        Map<String, String> fileMap = new HashMap<String, String>();

        RedisUtils redisUtils = RedisUtils.getInstance();
        for (String fileName : fileNames) {
            if (fileName != null || !"".equals(fileName)) {
                String url = new String();
                // TODO: 2017/10/27 0027 水印图判断。水印图不缓存，每次都请求新的url
                if (style.indexOf("watermark") != -1) {
                    GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucket, fileName, HttpMethod.GET);
                    request.setProcess(style.toString());
                    request.setExpiration(expiration);
                    url = ossClient.generatePresignedUrl(request).toString();
                } else {
                    //图片路径缓存
                    if (!redisUtils.exists(StaticResource.SYS_CACHE_IMAGE_URL + fileName)) {
                        String[] styleArr = style.toString().split("/");
                        if ("image".equals(styleArr[0])) {
                            //获得访问路径
                            GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucket, fileName, HttpMethod.GET);
                            request.setProcess(style.toString());
                            request.setExpiration(expiration);
                            url = ossClient.generatePresignedUrl(request).toString();
                        }
                        if ("style".equals(styleArr[0])) {
                            StringBuffer stringBuffer = new StringBuffer();
                            stringBuffer.append("http://").append(bucket).append(".").append(endpoint).append("/").append(fileName).append("/").append(styleArr[1]);
                            url = stringBuffer.toString();
                        }
                        //将图片访问路径给缓存起来
                        redisUtils.set(StaticResource.SYS_CACHE_IMAGE_URL + fileName, url);
                        redisUtils.expire(StaticResource.SYS_CACHE_IMAGE_URL + fileName, 3600);
                    } else {
                        url = redisUtils.getString(StaticResource.SYS_CACHE_IMAGE_URL + fileName);
                    }
                }

                fileMap.put(fileName, url);
            } else {
                logger.info("=====================>>>>>>>>    文件名为空！！！");
            }
        }

        ossClient.shutdown();
        return fileMap;

    }

    /**
     * 返回压缩图片访问url
     *
     * @param fileNames
     * @param processConfig
     * @return
     */
    public static String imageProcess(String bucket, String fileNames, AliOssImageConfig processConfig, int time) {
        List<String> fileNameList = new ArrayList<String>();
        fileNameList.add(fileNames);
        List<AliOssImageConfig> aliOssImageConfigList = new ArrayList<AliOssImageConfig>();
        aliOssImageConfigList.add(processConfig);
        Map<String, String> urlMap = imageProcess(bucket, fileNameList, aliOssImageConfigList, time);
        String url = new String();
        Set<String> keySet = urlMap.keySet();
        for (String s : keySet) {
            url = urlMap.get(s);
        }
        return url;
    }

    /**
     * 返回压缩图片访问url
     *
     * @param fileNames
     * @param processConfig
     * @param time
     * @return
     */
    public static String imageProcess(String fileNames, AliOssImageConfig processConfig, int time) {
        return imageProcess(null, fileNames, processConfig, time);
    }

    /**
     * 返回压缩图片访问url
     *
     * @param fileNames
     * @param processConfig
     * @return
     */
    public static String imageProcess(String fileNames, AliOssImageConfig processConfig) {
        return imageProcess(null, fileNames, processConfig, 0);
    }

    /**
     * 返回压缩图片访问url
     *
     * @param fileNames
     * @param processConfig
     * @param time
     * @return
     */
    public static Map<String, String> imageProcess(List<String> fileNames, List<AliOssImageConfig> processConfig, int time) {
        return imageProcess(null, fileNames, processConfig, time);
    }

    /**
     * 返回压缩图片访问url
     *
     * @param fileNames
     * @param processConfig
     * @return
     */
    public static Map<String, String> imageProcess(List<String> fileNames, List<AliOssImageConfig> processConfig) {
        return imageProcess(null, fileNames, processConfig, 0);
    }

    /**
     * 返回压缩图片访问url
     *
     * @param fileNames
     * @return
     */
    public static Map<String, String> imageProcess(List<String> fileNames) {
        return imageProcess(null, fileNames, new ArrayList<AliOssImageConfig>(), 0);
    }
}